package cc.shacocloud.mirage.utils;

import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.reflection.ModifierUtil;
import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
import kotlin.reflect.full.KClasses;
import kotlin.reflect.jvm.KCallablesJvm;
import kotlin.reflect.jvm.ReflectJvmMapping;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.*;
import java.net.URI;
import java.net.URL;
import java.time.temporal.Temporal;
import java.util.*;

import static cc.shacocloud.mirage.utils.BasicType.PRIMITIVE_WRAPPER_MAP;

/**
 * 类工具类
 *
 * @author 思追(shaco)
 */
public class ClassUtil {
    
    /**
     * 数组类名的后缀：{@code “[]”}
     */
    public static final String ARRAY_SUFFIX = "[]";
    private static final Map<Class<?>, Object> DEFAULT_TYPE_VALUES;
    /**
     * 内部数组类名的前缀：{@code “[”}
     */
    private static final String INTERNAL_ARRAY_PREFIX = "[";
    /**
     * 内部非基元数组类名的前缀：{@code “[L”}
     */
    private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L";
    /**
     * 包分隔符：{@code '.'}
     */
    private static final char PACKAGE_SEPARATOR = '.';
    /**
     * 嵌套类分隔符：{@code '$'}
     */
    private static final char NESTED_CLASS_SEPARATOR = '$';
    /**
     * 路径分隔符字符： {@code '/'}.
     */
    private static final char PATH_SEPARATOR = '/';
    
    static {
        DEFAULT_TYPE_VALUES = Map.of(
                boolean.class, false,
                byte.class, (byte) 0,
                short.class, (short) 0,
                int.class, 0,
                long.class, 0L,
                float.class, 0F,
                double.class, 0D,
                char.class, '\0');
    }
    
    /**
     * 确定给定类的包的名称，例如 {@code java.lang.String} 类的“java.lang”
     */
    public static @NotNull String getPackageName(@NotNull Class<?> clazz) {
        return getPackageName(clazz.getName());
    }
    
    /**
     * 确定给定的完全限定类名的包名称，例如 {@code java.lang.String} 类名的“java.lang”
     */
    public static @NotNull String getPackageName(@NotNull String fqClassName) {
        int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
        return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
    }
    
    /**
     * 替换 {@code Class.forName()}，它还返回原语（例如“int”）和数组类名（例如“String[]”）的类实例。
     * 此外，它还能够以Java源代码样式解析嵌套类名（例如“java.lang.Thread.State”而不是“java.lang.Thread$State”）。
     *
     * @param name        类的名称
     * @param classLoader 要使用的类装入器（可以是 {@code null}，表示缺省类装入器）
     * @return 所提供名称的类实例
     * @throws ClassNotFoundException 如果未找到该类
     * @throws LinkageError           如果无法加载类文件
     * @see Class#forName(String, boolean, ClassLoader)
     */
    public static Class<?> forName(@NotNull String name, @Nullable ClassLoader classLoader)
            throws ClassNotFoundException, LinkageError {
        
        Class<?> clazz = BasicType.resolvePrimitiveClassName(name);
        if (clazz == null) {
            clazz = BasicType.COMMON_CLASS_CACHE.get(name);
        }
        if (clazz != null) {
            return clazz;
        }
        
        // java.lang.String[]
        if (name.endsWith(ARRAY_SUFFIX)) {
            String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
            Class<?> elementClass = forName(elementClassName, classLoader);
            return Array.newInstance(elementClass, 0).getClass();
        }
        
        // [Ljava.lang.String
        if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
            String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
            Class<?> elementClass = forName(elementName, classLoader);
            return Array.newInstance(elementClass, 0).getClass();
        }
        
        // [[I 或者 [[Ljava.lang.String
        if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
            String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
            Class<?> elementClass = forName(elementName, classLoader);
            return Array.newInstance(elementClass, 0).getClass();
        }
        
        ClassLoader clToUse = classLoader;
        if (clToUse == null) {
            clToUse = getDefaultClassLoader();
        }
        try {
            return Class.forName(name, false, clToUse);
        } catch (ClassNotFoundException ex) {
            int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
            if (lastDotIndex != -1) {
                String nestedClassName =
                        name.substring(0, lastDotIndex) + NESTED_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
                try {
                    return Class.forName(nestedClassName, false, clToUse);
                } catch (ClassNotFoundException ignore) {
                }
            }
            throw ex;
        }
    }
    
    /**
     * 确定由提供的名称标识的 {@link Class} 是否存在并且可以加载。如果类或其依赖项之一不存在或无法加载，将返回 {@code false}。
     *
     * @param className   要检查的类的名称
     * @param classLoader 要使用的类装入器（可以是 {@code null}，表示缺省类装入器）
     * @return 指定的类是否存在（包括其所有超类和接口）
     * @throws IllegalStateException 如果相应的类是可解析的，但类的继承层次结构中存在可读性不匹配（通常是模块定义中缺少依赖项声明，用于此处检查的类实现的超类或接口）
     */
    public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
        try {
            forName(className, classLoader);
            return true;
        } catch (IllegalAccessError err) {
            throw new IllegalStateException("类的继承层次结构中的可读性不匹配 [" + className + "]: " + err.getMessage(), err);
        } catch (Throwable ex) {
            // 通常为 ClassNotFoundException 或 NoClassDefFoundError...
            return false;
        }
    }
    
    /**
     * 返回要使用的默认类加载器：通常是线程上下文类加载器（如果可用）
     *
     * @return 默认的类加载器（如果甚至无法访问系统类加载器，则只有 {@code null}）
     * @see Thread#getContextClassLoader()
     * @see ClassLoader#getSystemClassLoader()
     */
    @Nullable
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
            // 无法访问线程上下文类加载器...
        }
        if (cl == null) {
            // 没有线程上下文类装入器 -> 使用此类的类装入器。
            cl = ClassUtil.class.getClassLoader();
            if (cl == null) {
                // getClassLoader（） 返回 null 表示引导程序类加载器
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable ex) {
                    // 无法访问系统类加载器
                }
            }
        }
        return cl;
    }
    
    /**
     * 确定提供的类是否是一个 <em>内部类</em>，即一个包围类的非静态成员。
     *
     * @return 如果提供的类是一个内层类的话返回 {@code true}
     * @see Class#isMemberClass()
     */
    public static boolean isInnerClass(@NotNull Class<?> clazz) {
        return (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers()));
    }
    
    /**
     * 检查目标类是否可以从原类转化<br>
     * 转化包括：<br>
     * 1、原类是对象，目标类型是原类型实现的接口<br>
     * 2、目标类型是原类型的父类<br>
     * 3、两者是原始类型或者包装类型（相互转换）
     *
     * @param targetType 目标类型
     * @param sourceType 原类型
     * @return 是否可转化
     */
    public static boolean isAssignable(Class<?> targetType, Class<?> sourceType) {
        if (null == targetType || null == sourceType) {
            return false;
        }
        
        // 对象类型
        if (targetType.isAssignableFrom(sourceType)) {
            return true;
        }
        
        // 基本类型
        if (targetType.isPrimitive()) {
            // 原始类型
            Class<?> resolvedPrimitive = BasicType.WRAPPER_PRIMITIVE_MAP.get(sourceType);
            return targetType.equals(resolvedPrimitive);
        } else {
            // 包装类型
            Class<?> resolvedWrapper = PRIMITIVE_WRAPPER_MAP.get(sourceType);
            return resolvedWrapper != null && targetType.isAssignableFrom(resolvedWrapper);
        }
    }
    
    /**
     * 是否为基本类型（包括包装类和原始类）
     *
     * @param clazz 类
     * @return 是否为基本类型
     */
    public static boolean isBasicType(Class<?> clazz) {
        if (null == clazz) {
            return false;
        }
        return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
    }
    
    /**
     * 是否为包装类型
     *
     * @param clazz 类
     * @return 是否为包装类型
     */
    public static boolean isPrimitiveWrapper(Class<?> clazz) {
        if (null == clazz) {
            return false;
        }
        return BasicType.WRAPPER_PRIMITIVE_MAP.containsKey(clazz);
    }
    
    /**
     * 如果类型为原始类型则返回对应包装类型，反之返回类型本身
     */
    public static Class<?> resolvePrimitiveIfNecessary(@NotNull Class<?> clazz) {
        return (clazz.isPrimitive() ? PRIMITIVE_WRAPPER_MAP.get(clazz) : clazz);
    }
    
    /**
     * 比较判断types1和types2两组类，如果types1中所有的类都与types2对应位置的类相同，或者是其父类或接口，则返回{@code true}
     *
     * @param types1 类组1
     * @param types2 类组2
     * @return 是否相同、父类或接口
     */
    public static boolean isAllAssignableFrom(Class<?>[] types1, Class<?>[] types2) {
        if (ArrayUtil.isEmpty(types1) && ArrayUtil.isEmpty(types2)) {
            return true;
        }
        if (null == types1 || null == types2) {
            // 任何一个为null不相等（之前已判断两个都为null的情况）
            return false;
        }
        if (types1.length != types2.length) {
            return false;
        }
        
        Class<?> type1;
        Class<?> type2;
        for (int i = 0; i < types1.length; i++) {
            type1 = types1[i];
            type2 = types2[i];
            if (isBasicType(type1) && isBasicType(type2)) {
                // 原始类型和包装类型存在不一致情况
                if (BasicType.unWrap(type1) != BasicType.unWrap(type2)) {
                    return false;
                }
            } else if (!type1.isAssignableFrom(type2)) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * 获得对象数组的类数组
     *
     * @param objects 对象数组，如果数组中存在{@code null}元素，则此元素被认为是Object类型
     * @return 类数组
     */
    @Contract(pure = true)
    public static Class<?> @NotNull [] getClasses(Object @NotNull ... objects) {
        Class<?>[] classes = new Class<?>[objects.length];
        Object obj;
        for (int i = 0; i < objects.length; i++) {
            obj = objects[i];
            if (null == obj) {
                classes[i] = Object.class;
            } else {
                classes[i] = obj.getClass();
            }
        }
        return classes;
    }
    
    /**
     * 获取类型实现的所有接口
     */
    public static @NotNull Set<Class<?>> getAllInterfacesForClassAsSet(@NotNull Class<?> clazz) {
        if (clazz.isInterface()) {
            return Collections.singleton(clazz);
        }
        Set<Class<?>> interfaces = new LinkedHashSet<>();
        Class<?> current = clazz;
        while (current != null) {
            Class<?>[] currentInterfaces = current.getInterfaces();
            Collections.addAll(interfaces, currentInterfaces);
            current = current.getSuperclass();
        }
        return interfaces;
    }
    
    
    /**
     * 使用其主要造函数（对于 Kotlin 类，可能声明默认参数）或其默认构造函数（对于常规 Java 类，需要标准的无参数设置）实例化类。
     * <p>
     * 请注意，如果给定不可访问（即非公共）构造函数，则此方法尝试设置可访问的构造函数。
     *
     * @param clazz 要实例化的类
     * @return 实例
     * @see Constructor#newInstance
     */
    public static <T> T instantiateClass(@NotNull Class<T> clazz) throws IllegalArgumentException {
        if (clazz.isInterface()) {
            throw new IllegalArgumentException("指定的类是一个接口，无法实例化！");
        }
        
        try {
            return instantiateClass(clazz.getDeclaredConstructor());
        } catch (NoSuchMethodException ex) {
            Constructor<T> ctor = findPrimaryConstructor(clazz);
            if (ctor != null) {
                return instantiateClass(ctor);
            }
            throw new RuntimeException("未找到默认构造函数", ex);
        } catch (LinkageError err) {
            throw new RuntimeException("无法解析的类定义", err);
        }
    }
    
    
    /**
     * 返回所提供类的主构造函数
     * <p>
     * 对于 Kotlin 类，这将返回与 Kotlin 主构造函数（如 Kotlin 规范中所定义）相对应的 Java 构造函数。
     * 否则，特别是对于非 Kotlin 类，这只返回 {@code null}。
     *
     * @param clazz 要检查的类
     * @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">文档</a>
     */
    @Nullable
    public static <T> Constructor<T> findPrimaryConstructor(@NotNull Class<T> clazz) {
        if (KotlinDetector.isKotlinType(clazz)) {
            return KotlinDelegate.findPrimaryConstructor(clazz);
        }
        return null;
    }
    
    /**
     * 使用给定构造函数实例化类的便捷方法
     * <p>
     * 请注意，如果给定不可访问（即非公共）构造函数，则此方法尝试设置可访问的构造函数，并支持具有可选参数和默认值的 Kotlin 类
     *
     * @param ctor 要实例化的构造函数
     * @param args 要应用的构造函数参数（对未指定的参数使用 {@code null}，支持 Kotlin 可选参数和 Java 基元类型）
     * @return 新的实例
     * @see Constructor#newInstance
     */
    public static <T> T instantiateClass(@NotNull Constructor<T> ctor, Object... args) {
        try {
            ctor.setAccessible(true);
            if (KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
                return KotlinDelegate.instantiateClass(ctor, args);
            } else {
                Class<?>[] parameterTypes = ctor.getParameterTypes();
                if (!(args.length <= parameterTypes.length)) {
                    throw new IllegalArgumentException("不能指定比构造函数参数更多的参数");
                }
                Object[] argsWithDefaultValues = new Object[args.length];
                for (int i = 0; i < args.length; i++) {
                    if (args[i] == null) {
                        Class<?> parameterType = parameterTypes[i];
                        argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
                    } else {
                        argsWithDefaultValues[i] = args[i];
                    }
                }
                return ctor.newInstance(argsWithDefaultValues);
            }
        } catch (InstantiationException ex) {
            throw new RuntimeException("这是一个抽象类，无法实例化", ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException("构造函数不可访问！", ex);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException("构造函数的参数非法", ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException("构造函数实例化引发异常", ex.getTargetException());
        }
    }
    
    /**
     * 返回给定对象类型的描述性名称：通常只是类名，但组件类型类名 + “[]”用于数组，以及 JDK 代理的已实现接口的附加列表
     *
     * @return 类的限定名
     */
    @Nullable
    public static String getDescriptiveType(@Nullable Object value) {
        if (value == null) {
            return null;
        }
        Class<?> clazz = value.getClass();
        if (Proxy.isProxyClass(clazz)) {
            String prefix = clazz.getName() + " implementing ";
            StringJoiner result = new StringJoiner(",", prefix, "");
            for (Class<?> ifc : clazz.getInterfaces()) {
                result.add(ifc.getName());
            }
            return result.toString();
        } else {
            return clazz.getTypeName();
        }
    }
    
    
    /**
     * 检查给定类型是否表示简单值类型
     * <p>
     * {@code Void} 和 {@code void} 不被视为简单值类型
     *
     * @param type 要检查的类型
     */
    public static boolean isSimpleValueType(Class<?> type) {
        return (Void.class != type && void.class != type &&
                (isBasicType(type) ||
                        Enum.class.isAssignableFrom(type) ||
                        CharSequence.class.isAssignableFrom(type) ||
                        Number.class.isAssignableFrom(type) ||
                        Date.class.isAssignableFrom(type) ||
                        Temporal.class.isAssignableFrom(type) ||
                        URI.class == type ||
                        URL.class == type ||
                        Locale.class == type ||
                        Class.class == type));
    }
    
    
    /**
     * 检查类是否为普通类
     * <p>
     * 普通类不是枚举，注解，接口，抽象类，匿名类，编译器生成的类
     */
    public static boolean isNormalClass(@NotNull Class<?> clazz) {
        return !clazz.isAnonymousClass() && !clazz.isAnnotation()
                && !clazz.isEnum() && !clazz.isInterface() && !isAbstract(clazz) && !clazz.isSynthetic();
    }
    
    /**
     * 检查类是否为抽象类
     */
    public static boolean isAbstract(@NotNull Class<?> clazz) {
        return Modifier.isAbstract(clazz.getModifiers());
    }
    
    /**
     * 获取不带限定包名称的类名
     */
    public static @NotNull String getShortName(@NotNull Class<?> clazz) {
        return getShortName(clazz.getTypeName());
    }
    
    /**
     * 获取不带限定包名称的类名
     */
    @NotNull
    public static String getShortName(@NotNull String className) {
        int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
        int nameEndIndex = className.length();
        String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
        shortName = shortName.replace(NESTED_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
        return shortName;
    }
    
    /**
     * 将类名转为资源路径地址，该资源路地址由类的包名作为路径名组成，即所有点 （'.'） 都替换为斜杠 （'/'）
     *
     * @param clazz {@link Class} 如果为空则返回空字符
     * @return 表示包名称的路径
     * @see ClassLoader#getResource
     * @see Class#getResource
     */
    @NotNull
    public static String classPackageAsResourcePath(@Nullable Class<?> clazz) {
        if (clazz == null) {
            return "";
        }
        String className = clazz.getName();
        int packageEndIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
        if (packageEndIndex == -1) {
            return "";
        }
        String packageName = className.substring(0, packageEndIndex);
        return packageName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
    }
    
    /**
     * 判断是否有Setter方法
     *
     * @param clazz 待测试类
     * @return true 则包含 反之不包含
     * @see #isSetMethod
     */
    public static boolean hasSetter(Class<?> clazz) {
        if (ClassUtil.isNormalClass(clazz)) {
            for (Method method : clazz.getMethods()) {
                if (isSetMethod(method)) {
                    // 检测包含标准的setXXX方法即视为标准的JavaBean
                    return true;
                }
            }
        }
        return false;
    }
    
    /**
     * 判断当前方法是否是 set 方法
     * <p>
     * set 方法必须满足以下几个条件
     * <ul>
     *     <li>方法名称必须以 set 为前缀，且 set 的下一个字符大写</li>
     *     <li>方法必须只有一个入参</li>
     * </ul>
     */
    public static boolean isSetMethod(@NotNull Method method) {
        return method.getName().startsWith("set")
                && method.getName().length() >= 4
                && Character.isUpperCase(method.getName().charAt(3))
                && method.getParameterCount() == 1;
    }
    
    /**
     * 指定类中是否有public类型字段(static字段除外)
     *
     * @param clazz 待测试类
     * @return 是否有public类型字段
     */
    public static boolean hasPublicField(Class<?> clazz) {
        if (ClassUtil.isNormalClass(clazz)) {
            for (Field field : clazz.getFields()) {
                if (ModifierUtil.isPublic(field) && !ModifierUtil.isStatic(field)) {
                    //非static的public字段
                    return true;
                }
            }
        }
        return false;
    }
    
    /**
     * 判断是否为Bean对象，判定方法是：
     *
     * <pre>
     *     1、是否存在只有一个参数的setXXX方法
     *     2、是否存在public类型的字段
     * </pre>
     *
     * @param clazz 待测试类
     * @return 是否为Bean对象
     * @see #hasSetter(Class)
     * @see #hasPublicField(Class)
     */
    public static boolean isBean(Class<?> clazz) {
        return hasSetter(clazz) || hasPublicField(clazz);
    }
    
    /**
     * 内部类，以避免在运行时对 Kotlin 的硬依赖
     */
    private static class KotlinDelegate {
        
        /**
         * 检索与 Kotlin 主构造函数对应的 Java 构造函数（如果有）
         *
         * @param clazz the {@link Class} of the Kotlin class
         * @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">文档</a>
         */
        @Nullable
        public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
            try {
                KFunction<T> primaryCtor = KClasses.getPrimaryConstructor(JvmClassMappingKt.getKotlinClass(clazz));
                if (primaryCtor == null) {
                    return null;
                }
                Constructor<T> constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor);
                if (constructor == null) {
                    throw new IllegalStateException("找不到 Kotlin 主构造函数的 Java 构造函数： " + clazz.getName());
                }
                return constructor;
            } catch (UnsupportedOperationException ex) {
                return null;
            }
        }
        
        /**
         * 使用提供的构造函数实例化 Kotlin 类
         *
         * @param ctor 要实例化的 Kotlin 类的构造函数
         * @param args 要应用的构造函数参数（如果需要，对未指定的参数使用 {@code null}）
         */
        public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
                throws IllegalAccessException, InvocationTargetException, InstantiationException {
            
            KFunction<T> kotlinConstructor = ReflectJvmMapping.getKotlinFunction(ctor);
            if (kotlinConstructor == null) {
                return ctor.newInstance(args);
            }
            
            if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))) {
                KCallablesJvm.setAccessible(kotlinConstructor, true);
            }
            
            List<KParameter> parameters = kotlinConstructor.getParameters();
            Map<KParameter, Object> argParameters = new HashMap<>(parameters.size(), 1);
            if (!(args.length <= parameters.size())) {
                throw new IllegalArgumentException("提供的参数数应小于构造函数参数数");
            }
            for (int i = 0; i < args.length; i++) {
                if (!(parameters.get(i).isOptional() && args[i] == null)) {
                    argParameters.put(parameters.get(i), args[i]);
                }
            }
            return kotlinConstructor.callBy(argParameters);
        }
    }
    
}
